﻿using System;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using DriveBeamEditor.Controls.General;
using DriveBeamEditor.Enums;
using JBeamLibrary.Tools;
using JBeamLibrary.Types;
using JBeamLibrary.VehicleParts.Elements;
using JBeamLibrary.VehicleParts.Enums;
using System.Drawing;
using JColor = JBeamLibrary.VehicleParts.Elements.Color;
using Color = System.Drawing.Color;

namespace DriveBeamEditor.Tools
{
    public static class UIHelper
    {
        /// <summary>Creates an editor for the given value type</summary>
        /// <param name="valueType"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static Control GetEditor(Type valueType, object defaultValue = null)
        {
            /* Color */
            if (valueType == typeof(JColor))
            {
                return new ColorEdit()
                {
                    EditValue = defaultValue
                };
            }

            /* Float3 */
            if (valueType == typeof(Float3))
            {
                return new Float3Edit()
                {
                    EditValue = defaultValue
                };
            }

            /* Boolean */
            if (valueType == typeof(bool))
            {
                return new CheckBox()
                {
                    Checked = defaultValue != null && defaultValue is bool && (bool)defaultValue
                };
            }

            /* DefaultBoolean */
            //if (valueType == typeof(DefaultBoolean))
            //{
            //    return new CheckBox()
            //    {
            //        CheckState =
            //            (defaultValue == null || (int)defaultValue == (int)DefaultBoolean.Undetermined || defaultValue == DBNull.Value)
            //                ? CheckState.Indeterminate
            //                : (int)defaultValue == (int)DefaultBoolean.True
            //                    ? CheckState.Checked
            //                    : CheckState.Unchecked
            //    };
            //}

            /* Numeric */
            if (new[] { typeof(byte), typeof(short), typeof(int), typeof(uint), typeof(ushort), typeof(float), typeof(decimal), typeof(long), typeof(double) }.Contains(valueType))
            {
                return new NumEdit()
                {
                    MaskType = NumericMasks.GetMaskTypeFromType(valueType),
                    EditValue = @"NULL"
                };
                //return new NumericUpDown()
                //{
                //    DecimalPlaces = 2,
                //    Minimum = decimal.MinValue,
                //    Maximum = decimal.MaxValue,
                //    Value = (defaultValue != null && defaultValue.GetType() == valueType) ? decimal.Parse(defaultValue.ToString()) : 0
                //};
            }

            /* Text */
            if (valueType == typeof(string))
            {
                return new TextBox()
                {
                    Text = defaultValue != null ? defaultValue.ToString() : string.Empty
                };
            }

            /* Enum */
            if (valueType.IsEnum)
            {
                var box = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList };
                box.Items.AddRange(valueType.GetEnumValues().Cast<object>().ToArray());
                return box;
            }

            /* Classes */
            if (valueType.IsClass)
            {
                return new TextBox()
                {
                    Text = defaultValue.ToStringOrEmpty()
                };
            }

            return null;
        }

        /// <summary>Returns the default value used for the given value type</summary>
        /// <param name="valueType"></param>
        /// <returns></returns>
        public static object GetDefaultValue(Type valueType)
        {
            /* Color */
            if (valueType == typeof(JColor))
                return JColor.Black;

            /* Float3 */
            if (valueType == typeof(Float3))
                return Float3.Origin;

            /* Boolean */
            if (valueType == typeof(bool))
                return false;

            /* DefaultBoolean */
            //if (valueType == typeof(DefaultBoolean))
            //    return DefaultBoolean.Undetermined;

            /* Numeric */
            if (new[] { typeof(byte), typeof(short), typeof(int), typeof(uint), typeof(ushort), typeof(float), typeof(decimal), typeof(long), typeof(double) }.Contains(valueType))
                return 0;

            /* Text */
            if (valueType == typeof(string))
                return string.Empty;

            /* Enum */
            if (valueType.IsEnum)
                return 0;

            /* Classes */
            if (valueType.IsClass)
                return string.Empty;

            return null;
        }

        /// <summary>Defines the value of an editor</summary>
        /// <param name="editor"></param>
        /// <param name="value"></param>
        public static void SetValue(Control editor, object value)
        {
            /* Color */
            if (editor is ColorEdit)
                ((ColorEdit)editor).EditValue = value;

            /* Float3Edit */
            if (editor is Float3Edit)
                ((Float3Edit)editor).EditValue = value;

            /* NumEdit */
            if (editor is NumEdit)
                ((NumEdit)editor).EditValue = value;

            /* Checkbox */
            if (editor is CheckBox)
            {
                //if (value is DefaultBoolean)
                //{
                //    ((CheckBox)editor).CheckState =
                //        value == DBNull.Value || (int)value == (int)DefaultBoolean.Undetermined
                //            ? CheckState.Indeterminate
                //            : (int)value == (int)DefaultBoolean.True
                //                ? CheckState.Checked
                //                : CheckState.Unchecked;
                //}
                //else
                //{
                ((CheckBox)editor).CheckState =
                    value != null && value != DBNull.Value && value is bool
                        ? (bool)value ? CheckState.Checked : CheckState.Unchecked
                        : CheckState.Indeterminate;
                //}
            }

            /* NumericUpDown */
            if (editor is NumericUpDown)
                ((NumericUpDown)editor).Value = decimal.Parse(value != null ? value.ToString().ToUpperInvariant() : GetDefaultValue(typeof(decimal)).ToString());

            /* TextBox */
            if (editor is TextBox)
                ((TextBox)editor).Text = value.ToStringOrEmpty();

            /* Enum */
            if (editor is ComboBox)
                ((ComboBox)editor).SelectedItem = value;
        }

        /// <summary>Parses the given string to extract a value out of it</summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public static object ValueFromString(string val)
        {
            /* No value */
            if (val == null) return null;

            /* Color */
            JColor jcVal;
            if (JColor.TryParse(val, out jcVal))
                return jcVal;

            /* Float3 */
            Float3 f3Val;
            if (Float3.TryParse(val, out f3Val))
                return f3Val;

            /* DefaultBoolean */
            //if (val.SafeEquals("DefaultBoolean.True"))
            //    return DefaultBoolean.True;
            //if (val.SafeEquals("DefaultBoolean.False"))
            //    return DefaultBoolean.False;
            //if (val.SafeEquals("DefaultBoolean.Undetermined"))
            //    return DefaultBoolean.Undetermined;

            /* Boolean */
            bool bVal;
            if (bool.TryParse(val, out bVal))
                return bVal;

            /* Numeric */
            decimal nVal;
            if (decimal.TryParse(val, out nVal))
                return nVal;

            /* Enum */
            var toks = val.Split('.');
            if (toks.Length >= 2)
            {
                var valStr = val.LastDelimitedPart(".");
                var typStr = val.Substring(0, val.LastIndexOf(valStr, StringComparison.Ordinal) - 1);
                var typ = TryGetTypeFromString(typStr);
                return typ == null ? val : Enum.Parse(typ, valStr);
            }

            /* Text */
            return val;
        }

        public static string ValueToString(object value)
        {
            var type = value.GetType();
            if (type.IsEnum)
                return string.Format("{0}.{1}", type.Name, value);
            return value.ToStringOrEmpty();
        }

        /// <summary>Try to obtain a type from a string</summary>
        /// <param name="typeString"></param>
        /// <returns></returns>
        public static Type TryGetTypeFromString(string typeString)
        {
            var typ = Type.GetType(typeString);
            if (typ != null) return typ;

            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                var typStr = string.Format("{0}, {1}", typeString, assembly.FullName);
                typ = Type.GetType(typStr);
                if (typ != null) return typ;

                /* JBeam Types & UI enums */
                if (assembly == Assembly.GetExecutingAssembly() || assembly.FullName.ToUpperInvariant().Contains("JBEAMLIBRARY"))
                {
                    foreach (var jType in assembly.GetTypes())
                    {
                        if (jType.FullName.EndsWith(typeString))
                        {
                            return jType;
                        }
                    }
                }
            }

            return null;
        }

        public static Color ColorFromJColor(JColor color)
        {
            return Color.FromArgb(color.A, color.R, color.G, color.B);
        }

        public static JColor JColorFromColor(Color color)
        {
            return new JColor(color.A, color.R, color.G, color.B);
        }
    }
}
